package wdrun

import (
	"gitee.com/yutiandou/wdtools/wdevent"
	"os"
	"os/signal"
)

type ArgsRunTime struct {
	args       *Args
	cmdInfo    []*Cmd
	globalArgs []*Parameter
	entity     *wdevent.Entity
}

var rt *ArgsRunTime

func init() {
	rt = NewArgsRuntime()
	OnInit(help)
	OnExit(func(context *wdevent.Context) error {
		//fmt.Println("application start exit...")
		return nil
	})
}

func NewArgsRuntime() *ArgsRunTime {
	entity := wdevent.NewEventEntity().RegisterGlobal(wdevent.Recover)
	return &ArgsRunTime{
		args:       ParseArgs(),
		cmdInfo:    make([]*Cmd, 0),
		globalArgs: make([]*Parameter, 0),
		entity:     entity,
	}
}
func AddGlobalParameter(key string, def interface{}, help string) {
	rt.globalArgs = append(rt.globalArgs, &Parameter{
		key:   key,
		value: ToString(def),
		help:  help,
	})
}
func OnInit(handle wdevent.EventHandle) {
	rt.entity.RegisterEvent(onInitArgsRuntime, handle)
}
func OnRunning(cmd *Cmd, handle wdevent.EventHandle) {
	rt.cmdInfo = append(rt.cmdInfo, cmd)
	rt.entity.RegisterEvent(cmd.cmd, handle)
}
func OnExit(handle wdevent.EventHandle) {
	rt.entity.RegisterEvent(onExitArgsRuntime, handle)
}
func Start() error {
	ctx := wdevent.NewContext().Insert(ApplicationArgs, rt.args).
		Insert(ApplicationCmd, rt.cmdInfo).
		Insert(ApplicationGlobalCmd, rt.globalArgs)
	for _, i := range rt.globalArgs {
		ctx.Insert(i.key, i.value)
	}
	for key, value := range rt.args.kv {
		ctx.Insert(key, value)
	}
	if err := rt.entity.Dispatch(onInitArgsRuntime, ctx); err != nil {
		return err
	}
	echan := make(chan error)
	go func() {
		for _, i := range rt.cmdInfo {
			if handle, ok := rt.args.cmd[i.cmd]; ok {
				for _, p := range i.args {
					ctx.Insert(p.key, p.value)
				}
				for k, v := range handle.kv {
					ctx.Insert(k, v)
				}
				if err := rt.entity.Dispatch(i.cmd, ctx); err != nil {
					echan <- err
					break
				}
			}
		}
		echan <- nil
	}()
	err := exitLister(echan)
	exitErr := rt.entity.Dispatch(onExitArgsRuntime, ctx)
	if err != nil {
		return err
	}
	return exitErr
}

//------------------------- help function ----------------------------

func GetCmdFromContext(ctx *wdevent.Context) []*Cmd {
	if res, ok := ctx.Load(ApplicationCmd); ok {
		return res.([]*Cmd)
	}
	return nil
}
func GetGlobalParameterFromContext(ctx *wdevent.Context) []*Parameter {
	if res, ok := ctx.Load(ApplicationGlobalCmd); ok {
		return res.([]*Parameter)
	}
	return nil
}
func GetArgsFromContext(ctx *wdevent.Context) *Args {
	if res, ok := ctx.Load(ApplicationArgs); ok {
		return res.(*Args)
	}
	return nil
}

func exitLister(echan <-chan error) error {
	sig := make(chan os.Signal)
	signal.Notify(sig, os.Interrupt, os.Kill)
	select {
	case <-sig:
		return nil
	case err := <-echan:
		return err
	}
}
